/*******************************************************************************
        
        copyright:		Copyright (C) Mike Reinhold 2010. All rights reserved
        				
        license:		BSD style: $(LICENSE)
        		
        author(s): 		Mike Reinhold and Phil DeMonaco.
        				
*******************************************************************************/
module tango.util.cipher.Cryptor;

import tango.stdc.stdarg;
import tango.util.cipher.Cipher;
import tango.util.cipher.BlockCryptor;
import tango.util.cipher.StreamCryptor;

/** Base Cryptor class for wrapping ciphers and their associated parameters */
class Cryptor {
	private {
		Cryptor _c;
	}

	/***************************************************************************
			
			reset
						
			Remarks:
			resets the cryptor and its cipher. If the cryptor has not been
			initialized an exception will be thrown.
			
	***************************************************************************/
	void reset() {
		if(_c is null) {
			invalid("Cryptor: Not initialized!");
		} else {
			_c.reset();
		}
	}

	/***************************************************************************
	
			name
						
			Returns:
			the name of the cryptor 
			
			Remarks:
		
			cryptor = BlockCryptor
			cipher = Serpent
			
			(which equates to the cipher name combined with the supplied parameters
	
	***************************************************************************/
	string name() {
		if(_c is null) {
			invalid("Cryptor: Not initialized!");
		} else {
			return _c.name;
		}
		return "Cryptor: Not initialized";
	}
	
	/***************************************************************************
		
			encrypt
						
			Returns:
			if the cryptor is in encrypt mode
				
	***************************************************************************/
	bool encrypt() {
		return _c.encrypt();
	}
	
	/***************************************************************************
	
			encrypt
			
			Params:
			input_ = The plaintext byte array to be encrypted
			resetPrior = If the Cryptor should reset prior to encrypting
			
			Returns:
			the resulting ciphertext based on the given plaintext
			
			Remarks:
			Encrypt the input plaintext byte array and return the encrypted 
			ciphertext.

	***************************************************************************/
	void[] encrypt(void[] input_, bool resetPrior = true) {
		if(_c is null) {
			invalid("Cryptor: Not initialized!");
			return input_;
		} else {
			return _c.encrypt(input_, resetPrior);
		}
	}

	/***************************************************************************
	
			decrypt
			
			Params:
			input_ = The ciphertext byte array to be decrypted
			resetPrior = If the Cryptor should reset prior to decrypting
			
			Returns:
			the resulting plaintext based on the given ciphertext
			
			Remarks:
			Decrypt the input ciphertext byte array and return the 
			plaintext

	***************************************************************************/
	void[] decrypt(void[] input_, bool resetPrior = true) {
		if(_c is null) {
			invalid("Cryptor: Not initialized!");
			return input_;
		} else {
			return _c.decrypt(input_, resetPrior);
		}
	}

	/***************************************************************************
	
		init
		
		Params:
		cipher_ = 		the cipher to be wrapped in the cryptor
		_argumetns = 	the CipherParameters to be used
		
		Remarks:
		If the cryptor is already initialized an exception will be thrown.

	***************************************************************************/
	void init(Cipher cipher_, CipherParameters[] _arguments) {
		if(_c is null) {
			if(cast(BlockCipher) cipher_) {
				initBlockCipher(cast(BlockCipher)cipher_, 
						cast(BlockCipherParameters[])_arguments);
			} else if(cast(StreamCipher) cipher_) {
				initStreamCipher(cast(StreamCipher)cipher_, 
						cast(StreamCipherParameters[])_arguments);
			} else {
				invalid("Cryptor: Invalid Cipher Type");
			}
		} else {
			invalid(name ~ ": Already Initialized");
		}
	}

	/***************************************************************************
	
			initBlockCipher
			
			Params:
			cipher_ = 		the block cipher to be wrapped in the cryptor
			_arguments = 	the CipherParameters to be used
			
			Remarks:
			The arguments array may contain any of the following: SymmetricKey, 
			IVParameters, BlockCipherMode, BlockCipherPadding, and 
			BlockCipherOrdering (two, input order and output order). If an
			invalid argument is given an exception will be thrown.			

	***************************************************************************/
	 void initBlockCipher(BlockCipher cipher_, BlockCipherParameters[] _arguments) {
		SymmetricKey key_;
		IVParameters iv_;
		BlockCipherMode mode_;
		BlockCipherPadding pad_;
		BlockCipherOrdering inord_;
		BlockCipherOrdering outord_;
		BlockCryptor bc_ = new BlockCryptor();

		//check the type of each cipher parameter and set the private storage
		for(int i = 0; i < _arguments.length; i++) {
			if(cast(SymmetricKey) _arguments[i]) {
				key_ = cast(SymmetricKey) _arguments[i];
			} else if(cast(IVParameters) _arguments[i]) {
				iv_ = cast(IVParameters) _arguments[i];
			} else if(cast(BlockCipherMode) _arguments[i]) {
				mode_ = cast(BlockCipherMode) _arguments[i];
			} else if(cast(BlockCipherPadding) _arguments[i]) {
				pad_ = cast(BlockCipherPadding) _arguments[i];
			} else if(cast(BlockCipherOrdering) _arguments[i]) {
				BlockCipherOrdering o = cast(BlockCipherOrdering) _arguments[i];

				if(inord_ is null) {
					inord_ = o;
				} else {
					outord_ = o;
				}
			} else if(!(_arguments[i] is null)) {
				invalid("Cryptor: Invalid parameter");
			}
		}
		
		//initialize the block cipher mode and cipher
		bc_.init(cipher_, key_, iv_, mode_, pad_, inord_,
				outord_);
		_c = bc_;
	}

	/**
	 * Initialize the internal Cryptor with a StreamCryptor instance
	 * 
	 * Params:
	 *     cipher_ = The stream cipher to wrap in the cryptor
	 *     _arguments = The parameters to apply to this cryptor
	 */
	/***************************************************************************
		
			initStreamCipher
			
			Params:
			cipher_ = 		the stream cipher to be wrapped in the cryptor
			_arguments = 	the CipherParameters to be used
			
			Remarks:
			Currently the arguments array may contain any of the following: 
			SymmetricKey, IVParameters. At this point stream ciphers are not
			fully implemented	

	***************************************************************************/
	void initStreamCipher(StreamCipher cipher_, StreamCipherParameters[] _arguments) {
		SymmetricKey key_;
		IVParameters iv_;
		StreamCryptor sc_ = new StreamCryptor();

		for(int i = 0; i < _arguments.length; i++) {
			if(cast(SymmetricKey) _arguments[i]) {
				key_ = cast(SymmetricKey) _arguments[i];
			} else if(cast(IVParameters) _arguments[i]) {
				iv_ = cast(IVParameters) _arguments[i];
			//TODO add other stream cipher parameters here!!
			} else if(!(_arguments[i] is null)) {
				invalid("Cryptor: Invalid parameter");
			}
		}

		sc_.init(cipher_, key_, iv_);
		_c = sc_;
	}

	/***************************************************************************
	
			invalid
			
			Params:
			msg = message to associate with the exception
			
			Remarks:
			Throws an InvalidArgument exception

	***************************************************************************/
	static void invalid(char[] msg) {
		throw new IllegalArgumentException(msg);
	}
	
	Cryptor dup() {
		Cryptor c = new Cryptor();
		c._c = this._c.dup;
		return c;
	}
}
